﻿using ConfigCreate.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using ConfigCreate.Attributes;
using System.Windows.Controls;
using ConfigCreate.Model;
using System.Windows;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.IO;
using System.ComponentModel;

namespace ConfigCreate
{
    /// <summary>
    /// 
    /// </summary>
    public static class ConverterInternal
    {
        /// <summary>
        /// 通过扫描类实例来给面板控件添加并设置控件
        /// </summary>
        /// <typeparam name="T">类实例</typeparam>
        /// <param name="content">面板控件</param>
        /// <param name="obj">类对象</param>
        /// <param name="errmsg">错误信息</param>
        /// <param name="scan_children">是否扫描子属性</param>
        /// <param name="null_for_create">如果子属性值为空是否自动创建</param>
        /// <param name="config_file">配置存储位置</param>
        /// <param name="action">添加控件后执行的代码</param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException">面板为空时报错</exception>
        /// <exception cref="ConfigException"></exception>
        public static bool ConverterObject<T>(this ControlContentPanel content, T obj, out string errmsg, bool scan_children = true, bool null_for_create = true, string? config_file = null, Action? action = null) where T : class
        {
            if (content == null)
                throw new ArgumentNullException(nameof(content));
            if (obj == null)
                throw new ConfigException("解释实例为Null");
            errmsg = string.Empty;
            Type obj_type = obj.GetType();
            List<Func<(bool, string)>>? func_list = new List<Func<(bool, string)>>();
            //还原数据
            RestoreBindingObject(obj, config_file);
            action?.Invoke();
            //是否有控件加入
            bool is_add_control = false;
            //
            foreach (var pi in obj_type.GetProperties())
            {
                if (!pi.CanRead)
                    continue;
                Type pitype = pi.PropertyType;
                string property_path = pi.Name;
                bool is_enable = pi.CanWrite;

                //查找到有忽略此属性特性跳过检测此属性
                if (pi.GetCustomAttribute(typeof(CreateIgnoreAttribute), true) != null)
                    continue;

                if (pi.GetCustomAttributes(typeof(CreateBaseAttribute), true).FirstOrDefault() is CreateBaseAttribute cbase) //包含控件类特性
                {
                    bool save_config = true;
                    if (pi.GetCustomAttribute(typeof(CreateIgnoreSaveConfigAttribute), true) != null)
                        save_config = false;
                    //添加控件
                    bool? result = AddControls(content, obj, cbase, obj_type, pitype, property_path, save_config, out errmsg);
                    if (result == false)
                    {
                        errmsg = $"{property_path} -> {errmsg}";
                        return false;
                    }
                    if (result == true) //成功添加控件
                    {
                        string header_name = cbase.HeaderName;
                        string group_name = cbase.GroupName;
                        string title = cbase.Title;

                        is_add_control = true;

                        //绑定设置
                        if (!SetBindingPanel(content, obj, obj_type, pi, property_path, header_name, group_name, save_config, ref errmsg))
                        {
                            errmsg = $"{property_path} -> {errmsg}";
                            return false;
                        }
                        //---------------------------------------------
                        //设置控件权限
                        foreach (CreateRolePermissionConrolAttribute role_panel in pi.GetCustomAttributes(typeof(CreateRolePermissionConrolAttribute), true))
                        {
                            content.SetRolePermissionPanel(header_name, group_name, property_path, role_panel.RoleName, role_panel.PermissionType);
                        }
                        //设置组的权限
                        foreach (CreateRolePermissionGroupAttribute role_group in pi.GetCustomAttributes(typeof(CreateRolePermissionGroupAttribute), true))
                        {
                            content.SetRolePermissionGroup(header_name, group_name, role_group.RoleName, role_group.PermissionType);
                        }
                        //设置大类别的权限
                        foreach (CreateRolePermissionHeaderAttribute role_header in pi.GetCustomAttributes(typeof(CreateRolePermissionHeaderAttribute), true))
                        {
                            content.SetRolePermissionHeader(header_name, role_header.RoleName, role_header.PermissionType);
                        }
                        //设置控件相互可见性操作
                        foreach (CreateVisibilityPanelAttribute vpanel in pi.GetCustomAttributes(typeof(CreateVisibilityPanelAttribute), true))
                        {
                            string hname = header_name;
                            string gname = group_name;
                            if (!string.IsNullOrEmpty(vpanel.RelevanceHeaderName))
                                hname = vpanel.RelevanceHeaderName ?? string.Empty;
                            if(!string.IsNullOrEmpty(vpanel.RelevanceGroupName))
                                gname = vpanel.RelevanceGroupName ?? string.Empty;
                            func_list.Add(() =>
                            {
                                if (!content.SetVisibilityPanel(header_name, group_name, property_path, hname, gname, vpanel.RelevancePropertieName, vpanel.CheckValue, vpanel.IsReversal))
                                {
                                    return (false, $"{property_path} -> 未找到目标控件【{hname} - {gname} - {vpanel.RelevancePropertieName}】");
                                }
                                return (true, string.Empty);
                            });
                        }
                        //设置组相互可见性操作
                        foreach (CreateVisibilityGroupAttribute vgroup in pi.GetCustomAttributes(typeof(CreateVisibilityGroupAttribute), true))
                        {
                            string hname = header_name;
                            string gname = group_name;
                            if (!string.IsNullOrEmpty(vgroup.CheckHeaderName))
                                hname = vgroup.CheckHeaderName ?? string.Empty;
                            if (!string.IsNullOrEmpty(vgroup.CheckGroupName))
                                gname = vgroup.CheckGroupName ?? string.Empty;
                            func_list.Add(() =>
                            {
                                if (!content.SetVisibilityGroup(hname, gname, header_name, group_name, property_path, vgroup.CheckValue, vgroup.IsReversal))
                                {
                                    return (false, $"{property_path} -> 未找到目标组【{hname} - {gname}】");
                                }
                                return (true, string.Empty);
                            });
                        }
                        //设置控件数值变更通知
                        if (pi.GetCustomAttribute(typeof(CreateEditNotificationPanelAttribute), true) is CreateEditNotificationPanelAttribute noti)
                        {
                            content.SetEditNotificationPanel(header_name, group_name, property_path, noti.Definition);
                        }
                        //设置组的Dock属性和宽高
                        if (pi.GetCustomAttribute(typeof(CreateDockPanelGroup), true) is CreateDockPanelGroup dgroup)
                        {
                            content.SetDockPanelGroup(header_name, group_name, dgroup.Dock, dgroup.GroupWidth, dgroup.GroupHeight, dgroup.ParentKey);
                        }
                        //添加DockPanel
                        foreach (CreateDockPanel dpanel in pi.GetCustomAttributes(typeof(CreateDockPanel), true))
                        {
                            content.AddDockPanel(header_name, dpanel.DockPanelKey, dpanel.Dock, dpanel.Width, dpanel.Height, dpanel.ParentKey);
                        }
                    }
                }
                else if(scan_children && pitype.IsClass && !pitype.Equals(typeof(string)) && !pitype.IsGenericType)  //其它属性，如果可启扫描子属性,并且是类且不是字符串
                {
                    object? children = pi.GetValue(obj);
                    //如果属性为空并且可启为空时创建实例就创建实际
                    if (children == null && null_for_create)
                    {
                        children = Activator.CreateInstance(pitype);
                        pi.SetValue(obj, children);
                    }
                    if (children != null)
                    {
                        if (!ConverterObject(content, children, out errmsg, scan_children, null_for_create))
                        {
                            errmsg = $"{property_path} -> {errmsg}";
                            return false;
                        }
                    }
                }
                //

            }
            if (is_add_control) //检查是否有检测函数
            {
                if(obj is ICheckParameters) //对象继承了ICheckParameters接口
                {
                    content.AddSelfCheckFunc(((ICheckParameters)content.GetSetObject(obj)).CheckParametersValid);
                }
                if(obj is IPreCompletion)
                {
                    content.AddPreCompletion(((IPreCompletion)content.GetSetObject(obj)).Completion);
                }
            }
            if (func_list.Count > 0)
            {
                foreach(var func in func_list)
                {
                    var (result, emsg) = func();
                    if(!result)
                    {
                        errmsg = emsg;
                        return false;
                    }
                }
                func_list.Clear();
            }
            func_list = null;

            return true;
        }

        /// <summary>
        /// 从配置文件还原数据
        /// </summary>
        /// <param name="obj">还原的对象</param>
        /// <param name="config_file">配置文件路径</param>
        public static void RestoreBindingObject(object obj, string? config_file)
        {
            Type obj_type = obj.GetType();
            //检查配置文件是否存在，如果存即载入
            if (!string.IsNullOrEmpty(config_file) && File.Exists(config_file))
            {
                Dictionary<GroupHeader, object?> list = new Dictionary<GroupHeader, object?>();
                string json = File.ReadAllText(config_file);
                List<GroupHeaderObject>? ghlist = JsonConvert.DeserializeObject<List<GroupHeaderObject>>(json);
                if (ghlist != null)
                {
                    foreach (var gho in ghlist)
                        list[gho.GroupHeader] = gho.Value;
                    ghlist.Clear();
                    ghlist = null;
                }
                foreach (var pi in obj_type.GetProperties())
                {
                    if (!pi.CanRead || !pi.CanWrite)
                        continue;
                    Type pitype = pi.PropertyType;
                    string property_path = pi.Name;
                    bool is_enable = pi.CanWrite;
                    //
                    if (pi.GetCustomAttributes(typeof(CreateBaseAttribute), true).FirstOrDefault() is CreateBaseAttribute cbase) //包含控件类特性
                    {
                        GroupHeader gh = new GroupHeader(cbase.HeaderName, cbase.GroupName, property_path);
                        if (list.ContainsKey(gh))
                        {
                            object? value = list[gh];
                            SetBindingObjectValue(obj, obj_type, pitype, pi, property_path, value);
                        }

                    }
                }
            }
            
        }

        private static void SetBindingObjectValue(object obj, Type obj_type, Type pitype, PropertyInfo pi, string property_path, object? value)
        {
            if (value == null)
            {
                if (!pitype.IsValueType || pitype.Equals(typeof(string)) || pitype.IsGenericType)
                    pi.SetValue(obj, value);
            }
            else
            {
                Type value_type = value.GetType();
                if (value_type.Equals(pitype))
                {
                    pi.SetValue(obj, value);
                }
                else if (value_type.Equals(typeof(JArray)) && pitype.IsGenericType)
                {
                    object? tl = ((JArray)value).ToObject(pitype);
                    if (tl != null)
                    {
                        pi.SetValue(obj, tl);
                    }
                }
                else if (pitype.IsEnum)
                {
                    try
                    {
                        object tobj = Convert.ToInt32(value);
                        pi.SetValue(obj, tobj);
                    }
                    catch { }
                }
                else if (!pitype.Equals(typeof(string)) && pitype.IsClass)
                {
                    try
                    {
                        string tjson = JsonConvert.SerializeObject(value);
                        object? tobj = JsonConvert.DeserializeObject(tjson, pitype);
                        pi.SetValue(obj, tobj);
                    }
                    catch { }
                }
                else
                {
                    var converter = TypeDescriptor.GetConverter(pitype);
                    if (converter.CanConvertFrom(value_type))
                    {
                        pi.SetValue(obj, converter.ConvertFrom(value));
                    }
                    else
                    {
                        try
                        {
                            object tobj = Convert.ChangeType(value, pitype);
                            pi.SetValue(obj, tobj);
                        }
                        catch { }
                    }

                }
            }
        }


        private static bool SetBindingPanel(ControlContentPanel content, object obj, Type obj_type, PropertyInfo pi, string property_path, string header_name, string group_name, bool save_config, ref string errmsg)
        {
            if (pi.GetCustomAttribute(typeof(CreateBindingPanelAttribute), true) is CreateBindingPanelAttribute bpanel)
            {
                if (string.IsNullOrEmpty(bpanel.BindingDataPropertyName))
                {
                    errmsg = $"[CreateBindingPanelAttribute]属性中的定义的“BindingDataPropertyName”属性为空，必须设置此属性来为绑定提供数据源";
                    return false;
                }
                var tpi = obj_type.GetProperty(bpanel.BindingDataPropertyName);
                if (tpi == null)
                {
                    errmsg = $"[CreateBindingPanelAttribute]属性中的定义属性名为“{bpanel.BindingDataPropertyName}”的属性不存在";
                    return false;
                }
                if (tpi.PropertyType.Equals(typeof(Func<object, GroupHeader, List<BindingData>>)))
                {
                    Func<object, GroupHeader, List<BindingData>>? binding_data_func = tpi.GetValue(obj) as Func<object, GroupHeader, List<BindingData>>;
                    content.AddOrSetBindingPanel(header_name, group_name, property_path, "", binding_data_func, bpanel.DefaultBinding, bpanel.ShowSwitchButton, save_config: save_config);
                }
                else if (tpi.PropertyType.Equals(typeof(List<BindingData>)))
                {
                    List<BindingData>? binding_data = tpi.GetValue(obj) as List<BindingData>;
                    content.AddOrSetBindingPanel(header_name, group_name, property_path, "", binding_data, bpanel.DefaultBinding, bpanel.ShowSwitchButton, save_config: save_config);
                }
                else
                {
                    errmsg = $"[CreateBindingPanelAttribute]属性中的定义属性名为“{bpanel.BindingDataPropertyName}”的属性的类型不符合";
                    return false;
                }

            }
            return true;
        }

        private static bool? AddControls(ControlContentPanel content, object obj, CreateBaseAttribute cbase, Type obj_type, Type pitype, string property_path, bool save_config, out string errmsg)
        {
            errmsg = string.Empty;
            if (cbase is CreateTextBoxAttribute tbox)
            {
                content.AddTextBox(tbox.HeaderName, tbox.GroupName, obj, property_path, tbox.Title, tbox.Placeholder, tbox.TextLenght, tbox.ShowClearButton, tbox.IsReadonly, tbox.IsMultiline, tbox.Height, tbox.GetGroupIndex, tbox.GetHeaderIndex, tbox.Necessary, tbox.Remark, save_config);
                return true;
            }
            else if (cbase is CreateNumericAttribute number)
            {
                ValidationRule? rule = null;
                if (!string.IsNullOrEmpty(number.ValidationRule))
                {
                    var pi = obj_type.GetProperty(number.ValidationRule);
                    if (pi == null)
                    {
                        errmsg = $"[CreateNumericAttribute]定义属性名【{number.ValidationRule}】的属性不存在";
                        return false;
                    }
                    if (!pi.PropertyType.Equals(typeof(ValidationRule)))
                    {
                        errmsg = $"[CreateNumericAttribute]定义属性【{number.ValidationRule}】的类型不是“ValidationRule”";
                        return false;
                    }
                    rule = (ValidationRule?)pi.GetValue(obj);
                }
                content.AddNumeric(number.HeaderName, number.GroupName, obj, property_path, number.Title, number.Placeholder, number.Places, number.Increment, number.MaxValue, number.MinValue, number.ValueFormat, number.ShowClearButton, number.IsReadonly, rule, number.GetGroupIndex, number.GetHeaderIndex, number.Necessary, number.Remark, save_config);
                return true;
            }
            else if (cbase is CreateToggleButtonAttribute tbut)
            {
                content.AddToggleButton(tbut.HeaderName, tbut.GroupName, obj, property_path, tbut.Title, tbut.CheckedText, tbut.UnCheckedText, tbut.GetGroupIndex, tbut.GetHeaderIndex, tbut.Necessary, tbut.Remark, save_config);
                return true;
            }
            else if (cbase is CreatePasswordBoxAttribute pbox)
            {
                content.AddPasswordBox(pbox.HeaderName, pbox.GroupName, obj, property_path, pbox.Title, pbox.Placeholder, pbox.PasswordLength, pbox.ShowClearButton, pbox.ShowClearButton, pbox.PasswordChar, pbox.GetGroupIndex, pbox.GetHeaderIndex, pbox.Necessary, pbox.Remark, save_config);
                return true;
            }
            else if (cbase is CreateMultipleComboBoxAttribute mcbox) //由于多选组合框是继承单选组合框，所以它选择放在前面判断
            {
                List<ComboBoxData>? tl = null;
                if (!pitype.IsGenericType || pitype.Name != "List`1")
                {
                    errmsg = $"[CreateMultipleComboBoxAttribute]属性类型必须为List<T>";
                    return false;
                }
                if (!GetComboBoxDataList(obj, obj_type, pitype.GenericTypeArguments[0], mcbox, "CreateMultipleComboBoxAttribute", ref tl, ref errmsg))
                    return false;
                content.AddMultipleComboBox(mcbox.HeaderName, mcbox.GroupName, obj, property_path, mcbox.Title, pitype.GenericTypeArguments[0], tl, mcbox.ShowSelectAllButton, mcbox.ShowClearButton, mcbox.GetGroupIndex, mcbox.GetHeaderIndex, mcbox.Necessary, mcbox.Remark, save_config);
                return true;
            }
            else if (cbase is CreateComboBoxAttribute cbox)
            {
                List<ComboBoxData>? tl = null;
                if (!GetComboBoxDataList(obj, obj_type, pitype, cbox, "CreateComboBoxAttribute", ref tl, ref errmsg))
                    return false;

                content.AddComboBox(cbox.HeaderName, cbox.GroupName, obj, property_path, cbox.Title, pitype, tl, cbox.GetGroupIndex, cbox.GetHeaderIndex, cbox.Necessary, cbox.Remark, save_config);
                return true;
            }
            else if (cbase is CreateDatePickerAttribute date)
            {
                content.AddDatePicker(date.HeaderName, date.GroupName, obj, property_path, date.Title, date.GetGroupIndex, date.GetHeaderIndex, date.Necessary, date.Remark, save_config);
                return true;
            }
            else if (cbase is CreateDateTimePickerAttribute time)
            {
                content.AddDateTimePicker(time.HeaderName, time.GroupName, obj, property_path, time.Title, time.DateTimeFormat, time.GetGroupIndex, time.GetHeaderIndex, time.Necessary, time.Remark, save_config);
                return true;
            }
            else if (cbase is CreateIpAddressTextBoxAttribute ip)
            {
                content.AddIpAddressTextBox(ip.HeaderName, ip.GroupName, obj, property_path, ip.Title, ip.IsReadOnly, ip.GetGroupIndex, ip.GetHeaderIndex, ip.Necessary, ip.Remark, save_config);
                return true;
            }
            else if (cbase is CreateOpenFileAttribute open)
            {
                content.AddOpenFile(open.HeaderName, open.GroupName, obj, property_path, open.Title, open.DialogTitle, open.DialogFilter, open.IsMultiselect, open.IsReadonly, open.GetGroupIndex, open.GetHeaderIndex, open.Necessary, open.Remark, save_config);
                return true;
            }
            else if (cbase is CreateSaveFileAttribute save)
            {
                content.AddSaveFile(save.HeaderName, save.GroupName, obj, property_path, save.Title, save.DialogTitle, save.DialogFilter, save.IsReadonly, save.DefaultExt, save.InitialDirectory, save.GetGroupIndex, save.GetHeaderIndex, save.Necessary, save.Remark, save_config);
                return true;
            }
            else if (cbase is CreateOpenFolderAttribute folder)
            {
                content.AddOpenFolder(folder.HeaderName, folder.GroupName, obj, property_path, folder.Title, folder.DialogDescription, folder.ShowNewFolderButton, folder.RootFolder, folder.IsReadonly, folder.GetGroupIndex, folder.GetHeaderIndex, folder.Necessary, folder.Remark, save_config);
                return true;
            }
            else if (cbase is CreateTextBlockAttribute block)
            {
                content.AddTextBlock(block.HeaderName, block.GroupName, obj, property_path, block.StyleKey, block.HorizontalAlignment, block.FontSize, block.Foreground, block.Background, block.FontWeight, block.FontFamily, block.GetHeaderIndex, block.HeaderIndex, block.Remark, save_config);
                return true;
            }
            else if (cbase is CreateCustomControlAttribute custom)
            {
                content.AddCustomControl(custom.HeaderName, custom.GroupName, obj, property_path, custom.Title, custom.GroupIndex, custom.GetHeaderIndex, custom.Remark, save_config);
                return true;
            }

            return null;
        }

        private static bool GetComboBoxDataList(object obj, Type obj_type, Type pitype, CreateComboBoxAttribute cbox, string name, ref List<ComboBoxData>? tl, ref string errmsg)
        {
            if (pitype.IsEnum)  //枚举类型
            {
                tl = new List<ComboBoxData>();
                foreach (var item in Enum.GetValues(pitype))
                {
                    if (item != null)
                    {
                        FieldInfo? field = pitype.GetField(item.ToString() ?? string.Empty);
                        if (field != null && field.GetCustomAttribute(typeof(ComboBoxItemNameAttribute)) != null)
                        {
                            var cname = field.GetCustomAttribute(typeof(ComboBoxItemNameAttribute)) as ComboBoxItemNameAttribute;
                            if (cname != null)
                                tl.Add(new ComboBoxData(cname.ItemName, item));
                        }
                        else
                        {
                            tl.Add(new ComboBoxData(item.ToString() ?? string.Empty, item));
                        }
                    }
                }
            }
            else //非枚举类型
            {
                if (string.IsNullOrEmpty(cbox.ItemListPropertyName))
                {
                    errmsg = $"[{name}]属性不是枚举类型必须得设置【ItemListPropertyName】属性";
                    return false;
                }
                var pi = obj_type.GetProperty(cbox.ItemListPropertyName);
                if (pi == null)
                {
                    errmsg = $"[{name}]定义属性名【{cbox.ItemListPropertyName}】的属性不存在";
                    return false;
                }
                if (!pi.PropertyType.Equals(typeof(List<ComboBoxData>)))
                {
                    errmsg = $"[{name}]定义属性【{cbox.ItemListPropertyName}】的类型不是“List<ComboBoxData>”";
                    return false;
                }
                tl = (List<ComboBoxData>)(pi.GetValue(obj) ?? new List<ComboBoxData>());
            }

            return true;
        }
    }
}
